/*
 * image_packer.cpp
 *
 *  Created on: 2009-10-9
 *      Author: toxic
 */

#include "image_packer.h"
#include "utility.h"
#include "regex.h"
#include "toxic.h"
#include <stdint.h>
#include <fstream>
#include <algorithm>

using namespace toxic;
using namespace std;

struct TraverseInfo {
	TraverseInfo(vector<string>& _result, const set<string>& _filters)
	:result(_result)
	,filters(_filters) {

	}

	vector<string>& result;
	const set<string>& filters;
};


static void traverse_directory_impl(const file_info& fi, void* arg) {
	if (fi.is_regular()) {
		TraverseInfo* ti = static_cast<TraverseInfo*>(arg);
		foreach (const string& filter, ti->filters) {
			string filename = toxic::basename(fi.pathname);
			toxic::toLowerCase(filename);
			if (wildcard_match(filter.c_str(), filename.c_str())) {
				ti->result.push_back(fi.pathname);
				break;
			}
		}
	}
}

static void traverse_directory(TraverseInfo& ti, const string& pathname, size_t depth) {
	toxic::traverse_file(pathname.c_str(), traverse_directory_impl, &ti, depth);
}

static bool compare_pathname(const string& lhs, const string& rhs) {
	return toxic::basename(lhs) < toxic::basename(rhs);
}


void ImagePacker::pack(const PackConfig& config) {
	vector<string> filelist;
	set<string> filters;

	toxic::split(filters, config.filters, "/ ");
	TraverseInfo ti(filelist, filters);
	size_t depth = (config.isRecursive ? 0 : 1);

	traverse_directory(ti, config.inputPathname, depth);

	if (filelist.size() <= 0) {
		xwarn << "no matched file";
		return;
	}

	if (!toxic::createDirectory(config.outputPathname)) {
		xerror << "create output directory failed";
		return;
	}

	string outputImagePathname = config.outputPathname + "test.imgx";
	string outputHeaderPathname = config.outputPathname + "test.h";

	ofstream outputImage(outputImagePathname.c_str(), ios::binary);

	ByteStream bs(LITTLE_ENDIAN_);
	bs << uint32_t(0x5040DEAD) << uint16_t(0x0100) << uint16_t(filelist.size());
	outputImage.write((const char*)bs.data(), bs.size());

	sort(filelist.begin(), filelist.end(), compare_pathname);

	vector<string> enumTexts;

	uint16_t id = 0;
	foreach (const string& pathname, filelist) {
		xtrace << pathname;
		string filename = toxic::basename(pathname);
		vector<string> tokens;
		size_t tokenCount = toxic::split(tokens, filename, "_.");
		if (tokenCount == 1) {
			tokenCount = 2;
		}
		string text("EImage");
		for (size_t i = 0; i < tokenCount - 1; ++i) {
			string& str = tokens[i];
			str[0] = toupper(str[0]);
			text += str;
		}
		enumTexts.push_back(text);
		xtrace << text;

		ifstream in(pathname.c_str(), ios::ate | ios::binary);
		ByteStream bs(LITTLE_ENDIAN_);
		bs << id << uint8_t(filename.size());
		bs.write(filename.data(), filename.size());
		bs << uint32_t(in.tellg());

		outputImage.write((const char*)bs.data(), bs.size());

		in.seekg(0);
		char* buffer = new char[10240];
		std::streamsize length = 0;
		while (!in.eof()) {
			in.read(buffer, 10240);
			length = in.gcount();
			if (length > 0) {
				outputImage.write(buffer, length);
			}
		}
		delete [] buffer;
		in.close();
		++id;
	}

	outputImage.close();


	ofstream outputHeader(outputHeaderPathname.c_str());
	outputHeader << "#ifndef TEST_H" << endl;
	outputHeader << "#define TEST_H" << endl;
	outputHeader << "\nenum {" << endl;

	size_t size = enumTexts.size();
	for (size_t i = 0; i < size; ++i) {
		outputHeader << '\t' << enumTexts[i];
		if (i != size - 1) {
			outputHeader << ',';
		}
		outputHeader << endl;
	}

	outputHeader << "};\n" << endl;
	outputHeader << "#endif /* TEST_H */" << endl;
	outputHeader.close();
}
